package com.bilibili.juc.LockSupportTest;

import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
//Lock Support是用来创建锁和其他同步类的基本线程阻塞原语。
// Lock Support是一个线程阻塞工具类， 所有的方法都是静态方法， 可以让线程在任意位置阻塞， 阻塞之后也有对应的唤醒方法。归根结
// 底， Lock Support调用的Unsafe中的native代码。
//
// Lock Support提供park() 和unpark() 方法实现阻塞线程和解除线程阻塞的过程
// Lock Support和每个使用它的线程都有一个许可(permit) 关联。
// 每个线程都有一个相关的permit， permit最多只有一个， 重复调用un park也不会积累凭证。
//
// 形象的理解
// 线程阻塞需要消耗凭证(permit) ， 这个凭证最多只有1个。
// 当调用方法时
// *如果有凭证，则会直接消耗掉这个凭证然后正常退出；
// *如果无凭证，就必须阻塞等待凭证可用；
// 而****则相反， 它会增加一个凭证， 但凭证最多只能有1个， 累加无效。

//面试题
// 1:为什么可以突破wait/notify的原有调用顺序?
// 因为un park获得了一个凭证， 之后再调用park方法， 就可以名正言顺的凭证消费， 故不会阻塞。
// 先发放了凭证后续可以畅通无阻。
//
// 2:为什么唤醒两次后阻塞两次，但最终结果还会阻塞线程?
// 因为凭证的数量最多为1， 连续调用两次un park和调用一次un park效果一样， 只会增加一个凭证；
// 而调用两次park却需要消费两个凭证， 证不够， 不能放行。
/**
 * @author 小泽
 * @create 2022-09-21  10:57
 * 记得每天敲代码哦
 */
public class LockSupportDemo3 {
    public static void main(String[] args) {
        
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "\t----------come in" + "\t" + System.currentTimeMillis());
            LockSupport.park();
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t----------被唤醒了" + "\t" + System.currentTimeMillis());
        }, "t1");
        t1.start();

        new Thread(() -> {
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName() + "\t-----发出通知，去唤醒t1");
        }, "t2").start();
    }
}
//t2  -----发出通知，去唤醒t1
//t1  ----------come in  1654750970677--------------------卡在这里了

